package edu.umd.fosite.geom;

/**
 * Note that methods that return a vector modify this {@link Vec} in-place.
 * 
 */
public class Vec implements Cloneable {

	/**
	 * The three values of the vector.
	 */
	public double X, Y, Z;

	/**
	 * Creates a vector from three explicit coordinates.
	 * @param X
	 * @param Y
	 * @param Z
	 */
	public Vec(double X, double Y, double Z) {
		this.X = X;
		this.Y = Y;
		this.Z = Z;
	}

	/**
	 * Creates a vector from an array containing the three coordinates.
	 * @param a
	 */
	public Vec(double[] a) {
		if (a.length != 3)
			throw new RuntimeException(
					"argument to Vec() must have exactly three components");
		this.X = a[0];
		this.Y = a[1];
		this.Z = a[2];
	}

	/**
	 * The magnitude of the vector is its dot product with itself.
	 * @return The magnitude of this vector
	 */
	public double magnitude() {
		return Math.sqrt(X * X + Y * Y + Z * Z);
	}

	/**
	 * Multiply all coordinates by a constant scale factor.
	 * 
	 * @param f
	 */
	public Vec scale(double f) {
		X *= f;
		Y *= f;
		Z *= f;
		return this;
	}

	/**
	 * Normalize this vector, so that its magnitude is unity.
	 * 
	 * @return The same vector, normalized.
	 */
	public Vec normalize() {
		double m = magnitude();
		return scale(1 / m);
	}

	public Vec negate() {
		X = -X;
		Y = -Y;
		Z = -Z;
		return this;
	}

	/**
	 * Adds two vectors.
	 * 
	 * @param v
	 * @return This vector, summed with the vector passed in.
	 */
	public Vec add(Vec v) {
		X += v.X;
		Y += v.Y;
		Z += v.Z;
		return this;
	}

	public double dot(Vec v) {
		return dotProduct(v);
	}

	public double dotProduct(Vec v) {
		return X * v.X + Y * v.Y + Z * v.Z;
	}

	public Vec cross(Vec v) {
		return crossProduct(v);
	}

	public Vec crossProduct(Vec v) {
		double x = X, y = Y, z = Z;
		X = y * v.Z - v.Y * z;
		Y = v.X * z - v.Z * x;
		Z = x * v.Y - v.X * y;
		return this;
	}

	public double[] toArray() {
		double[] ret = { X, Y, Z };
		return ret;
	}
	
	public String toString() {
		return "Vec[" + X + "," + Y + "," + Z + "]";
	}
	
	public Vec clone() {
		return new Vec(X, Y, Z);
	}

}
